package bmaso.purest;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * <p>
 * Describes the pattern of an expected name/value pair, and is able to
 * parse a "name=value" string to determine if it matches the pattern.
 * Each pattern may optionally define a "binding", which is a string
 * name and a parsed value.
 * </p>
 *
 * <p>
 * The strings passed to the constructor describe the expected name/value
 * pairs. The strings have two parts, the <i>name-pattern</i> and the
 * <i>value-pattern</i>. The constructor accepts the name- and
 * value-pattern in a single string, like so: <tt><b> 
 * "<i>name-pattern</i>:<i>value-pattern</i>".
 * </p>
 * 
 * <h3>Name Pattern</h3>
 * 
 * <p>
 * The name-pattern portion indicates the value of the <i>name</i> portion
 * of a name/value pair which must be present. The name-pattern also optionally
 * defines the binding name (see {@link #getBindingName}) -- if the binding
 * name is not present then the <i>name</i> is take also as the binding name.
 * </p>
 * 
 * <p>
 * Some examples of name-patterns and the required name and the binding name:
 * </p>
 * 
 * <table>
 *   <tr>
 *     <th>Name-pattern</th>
 *     <th>Required name</th>
 *     <th>Binding name</th>
 *     <th>Examples</th>
 *   </tr>
 *   
 *   <tr>
 *     <td><tt><b>color</b></tt></td>
 *     <td>"color"</td>
 *     <td>"color"</td>
 *     <td><tt><b>"color:<i>value-pattern</i>"</b></tt></td>
 *   </tr>
 *
 *   <tr>
 *     <td><tt><b>foregroundColor@color</b></tt></td>
 *     <td>"color"</td>
 *     <td>"foregroundColor</td>
 *     <td><tt><b>"foregroundColor@color:<i>value-pattern"</b></tt></td>
 *   </tr>
 * </table>
 * 
 * <h3>Value Pattern</h3>
 * 
 * <p>
 * The value pattern describe acceptable patterns of parameter values that
 * will be matched by this NameValuePattern object. The value-pattern is
 * interpreted as a regex pattern value by default. The special pattern "?"
 * is a wildcard that will match any parameter value. The wildcard
 * is equivalent to the value-pattern ".*".
 * </p>
 */
public class NameValuePattern {
	private String parameterName;
	private String bindingName;
	private String matchedValue;
	private boolean fOptional;
	
	private Pattern valuePattern;
	
	private static final Pattern nameValueRegexPattern =
		Pattern.compile("\\??\\{([^:@\\}]+)(?:@([^:\\}]+))?(?::([^\\}]*))?\\}");
	
	public NameValuePattern(String pattern)
	{
		Matcher m = nameValueRegexPattern.matcher(pattern);
		if(!m.matches())
		{
			throw new IllegalURIPatternException("The name/value pattern " +
					pattern + " is not recognized as a valid name/value pattern");
		}
		
		bindingName = m.group(1);
		parameterName = m.group(2);
		if(parameterName == null || parameterName.length() == 0)
		{
			parameterName = bindingName;
		}

		String p = m.group(3);
		if(p == null || p.length() == 0 || p.equals("?"))
		{
			p = ".*";
		}
		valuePattern = Pattern.compile(p);
		
		if(m.group(0).startsWith("?")) {
			fOptional = true;
		}
	}
	
	public boolean matches(String candidateValue)
	{
		reset();
		
		Matcher valueMatcher = valuePattern.matcher(candidateValue);
		if(valueMatcher.matches())
		{
			matchedValue = valueMatcher.group(0);
			return true;
		}
		return false;
	}
	
	public boolean matchesAny(Iterable<String> candidateValues) {
		for(String candidateValue : candidateValues) {
			if(matches(candidateValue)) {
				return true;
			}
		}
		return false;
	}

	public String getParameterName() {
		return parameterName;
	}

	public void setParameterName(String parameterName) {
		this.parameterName = parameterName;
	}

	public String getBindingName() {
		return bindingName;
	}

	public void setBindingName(String bindingName) {
		this.bindingName = bindingName;
	}

	public String getMatchedValue() {
		return matchedValue;
	}

	public void setMatchedValue(String matchedValue) {
		this.matchedValue = matchedValue;
	}
	
	public boolean isOptional() {
		return fOptional;
	}
	
	public void setOptional(boolean optional) {
		fOptional = optional;
	}
	
	public void reset()
	{
		matchedValue = null;
	}
}
